Bun TanStack Start Run TanStack Start (full-stack React framework) with Bun. Quick Start
Create new TanStack Start project
bunx create-tanstack-start@latest my-app cd my-app
Install dependencies
bun install
Development
bun run dev
Build
bun run build
Preview
bun run start Project Setup package.json { "scripts" : { "dev" : "vinxi dev" , "build" : "vinxi build" , "start" : "vinxi start" } , "dependencies" : { "@tanstack/react-router" : "^1.139.0" , "@tanstack/start" : "^1.120.0" , "react" : "^19.2.0" , "react-dom" : "^19.2.0" , "vinxi" : "^0.5.10" } } app.config.ts import { defineConfig } from "@tanstack/start/config" ; export default defineConfig ( { server : { preset : "bun" , } , } ) ; File-Based Routing app/ ├── routes/ │ ├── __root.tsx # Root layout │ ├── index.tsx # / │ ├── about.tsx # /about │ ├── users/ │ │ ├── index.tsx # /users │ │ └── $userId.tsx # /users/:userId │ └── api/ │ └── users.ts # /api/users └── client.tsx Route Components Basic Route // app/routes/index.tsx import { createFileRoute } from "@tanstack/react-router" ; export const Route = createFileRoute ( "/" ) ( { component : Home , } ) ; function Home ( ) { return < h1
Welcome Home </ h1
; } Route with Loader // app/routes/users/index.tsx import { createFileRoute } from "@tanstack/react-router" ; export const Route = createFileRoute ( "/users/" ) ( { loader : async ( ) => { const response = await fetch ( "/api/users" ) ; return response . json ( ) ; } , component : Users , } ) ; function Users ( ) { const users = Route . useLoaderData ( ) ; return ( < ul
{ users . map ( ( user ) => ( < li key = { user . id }
{ user . name } </ li
) ) } </ ul
) ; } Dynamic Routes // app/routes/users/$userId.tsx import { createFileRoute } from "@tanstack/react-router" ; export const Route = createFileRoute ( "/users/$userId" ) ( { loader : async ( { params } ) => { const response = await fetch (
/api/users/ ${ params . userId }) ; return response . json ( ) ; } , component : UserDetail , } ) ; function UserDetail ( ) { const user = Route . useLoaderData ( ) ; const { userId } = Route . useParams ( ) ; return ( < div< h1
{ user . name } </ h1
< p
User ID: { userId } </ p
</ div
) ; } Server Functions Define Server Function // app/routes/users/index.tsx import { createFileRoute } from "@tanstack/react-router" ; import { createServerFn } from "@tanstack/start" ; import { Database } from "bun:sqlite" ; const getUsers = createServerFn ( "GET" , async ( ) => { const db = new Database ( "data.sqlite" ) ; const users = db . query ( "SELECT * FROM users" ) . all ( ) ; db . close ( ) ; return users ; } ) ; const createUser = createServerFn ( "POST" , async ( name : string ) => { const db = new Database ( "data.sqlite" ) ; db . run ( "INSERT INTO users (name) VALUES (?)" , [ name ] ) ; db . close ( ) ; return { success : true } ; } ) ; export const Route = createFileRoute ( "/users/" ) ( { loader : ( ) => getUsers ( ) , component : Users , } ) ; function Users ( ) { const users = Route . useLoaderData ( ) ; const handleSubmit = async ( e : React . FormEvent < HTMLFormElement
) => { e . preventDefault ( ) ; const formData = new FormData ( e . currentTarget ) ; const name = formData . get ( "name" ) as string ; await createUser ( name ) ; // Refetch or update state } ; return ( < div
< form onSubmit = { handleSubmit }
< input name = " name " placeholder = " Name " /> < button type = " submit "
Add User </ button
</ form
< ul
{ users . map ( ( user ) => ( < li key = { user . id }
{ user . name } </ li
) ) } </ ul
</ div
) ; } Server Function with Context import { createServerFn } from "@tanstack/start" ; import { getWebRequest } from "@tanstack/start/server" ; const getSession = createServerFn ( "GET" , async ( ) => { const request = getWebRequest ( ) ; const cookies = request . headers . get ( "Cookie" ) ; // Parse and validate session return { userId : "123" , role : "admin" } ; } ) ; const protectedAction = createServerFn ( "POST" , async ( data : any ) => { const session = await getSession ( ) ; if ( session . role !== "admin" ) { throw new Error ( "Unauthorized" ) ; } // Perform action return { success : true } ; } ) ; API Routes // app/routes/api/users.ts import { createAPIFileRoute } from "@tanstack/start/api" ; import { Database } from "bun:sqlite" ; export const Route = createAPIFileRoute ( "/api/users" ) ( { GET : async ( { request } ) => { const db = new Database ( "data.sqlite" ) ; const users = db . query ( "SELECT * FROM users" ) . all ( ) ; db . close ( ) ; return Response . json ( users ) ; } , POST : async ( { request } ) => { const { name } = await request . json ( ) ; const db = new Database ( "data.sqlite" ) ; db . run ( "INSERT INTO users (name) VALUES (?)" , [ name ] ) ; db . close ( ) ; return Response . json ( { success : true } ) ; } , } ) ; Root Layout // app/routes/__root.tsx import { createRootRoute , Link , Outlet } from "@tanstack/react-router" ; export const Route = createRootRoute ( { component : Root , } ) ; function Root ( ) { return ( < html
< head
< meta charSet = " utf-8 " /> < meta name = " viewport " content = " width=device-width, initial-scale=1 " /> < title
My App </ title
</ head
< body
< nav
< Link to = " / "
Home </ Link
< Link to = " /users "
Users </ Link
< Link to = " /about "
About </ Link
</ nav
< main
< Outlet /> </ main
</ body
</ html
) ; } Error Handling // app/routes/users/$userId.tsx export const Route = createFileRoute ( "/users/$userId" ) ( { loader : async ( { params } ) => { const response = await fetch (
/api/users/ ${ params . userId }) ; if ( ! response . ok ) { throw new Error ( "User not found" ) ; } return response . json ( ) ; } , errorComponent : ( { error } ) => ( < div< h1
Error </ h1
< p
{ error . message } </ p
</ div
) , pendingComponent : ( ) => < div
Loading... </ div
, component : UserDetail , } ) ; Search Params // app/routes/users/index.tsx import { createFileRoute } from "@tanstack/react-router" ; import { z } from "zod" ; const searchSchema = z . object ( { page : z . number ( ) . default ( 1 ) , limit : z . number ( ) . default ( 10 ) , search : z . string ( ) . optional ( ) , } ) ; export const Route = createFileRoute ( "/users/" ) ( { validateSearch : searchSchema , loader : async ( { search } ) => { const { page , limit , search : query } = search ; // Fetch with pagination return fetchUsers ( { page , limit , query } ) ; } , component : Users , } ) ; Deployment Build for Bun NITRO_PRESET = bun bun run build bun .output/server/index.mjs Docker FROM oven/bun:1 AS builder WORKDIR /app COPY package.json bun.lockb ./ RUN bun install --frozen-lockfile COPY . . RUN bun run build FROM oven/bun:1 WORKDIR /app COPY --from = builder /app/.output ./output EXPOSE 3000 CMD [ "bun" , ".output/server/index.mjs" ] Common Errors Error Cause Fix Cannot find bun:sqlite Wrong preset Set server.preset: "bun" Server function failed Network error Check function definition Route not found File naming Check route file location Hydration mismatch Server/client diff Check loader data When to Load References Load references/router-api.md when: Advanced routing patterns Route guards Nested layouts Load references/forms.md when: Form handling Mutations Optimistic updates